//
//  BleManager.swift
//  EPHome
//
//  Created by liujianlong on 2022/8/2.
//

import UIKit
import CoreBluetooth
var BLE = CCBleManager.share
@objc public protocol CCBleManagerDelegate : CBCentralManagerDelegate
{
    /// 系统底层已经连接的设备
    @objc optional func retrieveConnectedPeripherals(_ central:CBCentralManager,_ systemConnect:[CBPeripheral])->[CBPeripheral]
    /// 自动连接
    @objc optional func autoConnect(_ central:CBCentralManager,_ searchedPeripherals:[CBPeripheral])->[CBPeripheral]
    
    /// 过滤/筛选设备
    /// - Parameters:
    ///   - central: 中心设备
    ///   - peripheral: 扫描到外围设备
    ///   - data: kCBAdvDataManufacturerData
    ///   - RSSI: 信号
    /// - Returns: 返回 True ，能够搜索到该设备 ，返回false，过滤掉该外围设备
    @objc optional func filter(_ central:CBCentralManager, _ peripheral:CBPeripheral, _ data:[String:Any], _ RSSI:NSNumber)->Bool
    
    /// 连接成功，连接成功后，需要把外围设备做数据本地化，在这个方法里
    /// - Parameters:
    ///   - central: 中心设备
    ///   - peripheral: 连接的外围设备。
    @objc optional func conncted(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
    
    /// 断开连接，/连接失败回调，在这个方法里做断开连接的本地数据化更新
    /// - Parameters:
    ///   - central: 中心设备
    ///   - peripheral: 连接的外围设备
    ///   - error: 返回错误
    @objc optional func disConnected(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
}
@objc protocol CCBleManagerPeripheralDelegate : CBPeripheralDelegate
{}

@objc enum BLEStatus : Int
{
    case normal = 0
    case disconnect = 1
    case connecting = 2
    case connectedSucces = 3
    case connectedFail = 4
    case reConnecting = 5
    case DFU = 6
}

///蓝牙状态的通知
public let BLEStatusUpdateNotifyName = "BLEStatusUpdateNotifyName"

//MARK: - CBPeripheral扩展
private var macKey = "macKey"
private var advertDataKey = "advertDataKey"
extension CBPeripheral{
    @objc var mac : String{
        set {objc_setAssociatedObject(self, &macKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)}
        get{return objc_getAssociatedObject(self, &macKey) as? String ?? ""}
    }
    
    @objc var advertData : NSData{
        set {objc_setAssociatedObject(self, &advertDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)}
        get{return objc_getAssociatedObject(self, &advertDataKey) as? NSData ??  NSData()}
    }
}

//MARK: - 蓝牙中心管理
@objcMembers class CCBleManager : NSObject {
    static let share = CCBleManager()
    private override init() {}
    /// 扫描到的外围设备
    var searchedDevices:[CBPeripheral] = [CBPeripheral]()
    /// 连接成功的外围设备
    var connectedDevices:[CBPeripheral] = [CBPeripheral]()
    /// 服务
    var services = [CBUUID(string:"1F40EAF8-AAB4-14A3-F1BA-F61F35CDDBAA"),
                    CBUUID(string:"8EC9FE59-F315-4F60-9FB8-838830DAEA50"),
                    CBUUID(string:"A6ED0401-D344-460A-8075-B9E8EC90D71B"),
                    CBUUID(string:"190E")]
    
    var delegate:CCBleManagerDelegate?
    var dataSource:CCBleManagerPeripheralDelegate?
    ///实时扫描结果block
    var scanBlock:(()->Void)?
    
    var timetemp:Int = 0
    var timer : DispatchSourceTimer?
    lazy var central : CBCentralManager =
    {
        return CBCentralManager(delegate:self, queue: DispatchQueue.main)
    }()
    
    var status : BLEStatus = .normal{
        didSet{
            DispatchQueue.main.async {
                NotificationCenter.default.post(name:NSNotification.Name(BLEStatusUpdateNotifyName), object:nil)
            }
        }
    }
    
    /// 开始扫描
    func startScan(){
        ///每次扫描前，先把外围设备代理对象设置好，由BleDataTool实际处理外围设备各种回调
        self.dataSource = BleDataTool.share
        self.delegate = BleDataTool.share
        
        //        self.status = self.status == .DFU ? self.status : .normal
        
        if self.central.state == .poweredOn{
            /// 第一个参数：是否重复扫描已经发现的设备。如果需要不断获取最新信号强度RSSI，此参数要设为YES
            /// 第二个参数：蓝牙没打开，是否弹出提示框
            let option = [CBCentralManagerScanOptionAllowDuplicatesKey:false,
                               CBCentralManagerOptionShowPowerAlertKey:true]
            self.central.scanForPeripherals(withServices:self.services, options:option)
        }
        
        ///实时扫描结果block
        self.scanBlock?()
        
        ///5秒后停止扫描
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {[weak self] in
            self?.stopScan()
        }
    }
    
    /// 停止扫描
    func stopScan(){
        self.central.stopScan()
        self.scanBlock?()
    }
    
    
    ///重连上次连接的
    func reConnect(){
        self.status = .normal
        self.connect(self.connectedDevices.first)
    }
    
    /// 重接本地系统已连接的
    func localConnect(){
        /// 这里写个定时器，因为获取系统底层已经链接过的设备的接口retrieveConnectedPeripherals，有时候并不能马上获取的到设备。
        var array = self.systemConnected()
        if array.count <= 0 {
            weak var weakSelf = self
            self.timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
            self.timetemp = 0
            self.timer?.schedule(deadline: .now() + 1, repeating:.seconds(1))
            self.timer?.setEventHandler(handler: {
                if (weakSelf?.timetemp ?? 0 > 3) || array.count > 0{
                    weakSelf?.timetemp = 0
                    if weakSelf?.connectedDevices.count ?? 0 <= 0{
                        weakSelf?.localConnect()
                    }
                    weakSelf?.timer?.cancel()
                }else{
                    weakSelf?.timetemp += 1
                    array = weakSelf?.systemConnected() ?? [CBPeripheral]()}
            })
            self.timer?.resume()
            return
        }
        
        _ = self.delegate?.retrieveConnectedPeripherals?(self.central, array).compactMap{
            self.addSearchDevice($0)}
        
        let temp = self.delegate?.autoConnect?(self.central,self.searchedDevices)
        if temp?.count ?? 0 > 0{
            self.connect(temp?.first!)
        }
    }
    
    /// 连接指定的外围设备，通过MAC地址
    func connectMAC(_ mac:String){
        let peripheral = self.searchedDevices.filter{$0.mac == mac}.first
        self.connect(peripheral)
    }
    
    /// 连接指定的外围设备
    func connect(_ peripheral:CBPeripheral?=nil){
        guard let p = peripheral else{
            CCHud.start(LS("设备不在可搜索范围之内"), stopTime: Int32(1.5), handle: nil)
            self.startScan()
            return }
        if p.state == .connected{return}
        self.status = .connecting
        
        ///注意，这里APP只能同时只能连接一个设备，所以先把不是此次连接的对象设备，都断开。
        ///如果app功能需要多连，注释掉该方法
        self.connectedDevices.compactMap{
            if $0.identifier.uuidString != peripheral?.identifier.uuidString{
                self.central.cancelPeripheralConnection($0)}            
        }
        
        /// 第一个参数：在程序被挂起时，连接成功显示alert提醒框
        /// 第二个参数：在程序被挂起时，断开连接显示alert提醒框
        /// 第三个参数:   在程序被挂起时，显示所有的提醒消息
        let option = [CBConnectPeripheralOptionNotifyOnConnectionKey:true,
                   CBConnectPeripheralOptionNotifyOnDisconnectionKey:true,
                    CBConnectPeripheralOptionNotifyOnNotificationKey:true]
        self.central.connect(p, options:option)
    }
    
    /// 断开指定外围设备，通过MAC地址
    func disConnectMAC(_ mac:String){
        let peripheral = self.searchedDevices.filter{$0.mac == mac}.first
        self.disConnect(peripheral)
    }
    
    /// 断开连接指定外围设备
    func disConnect(_ peripheral:CBPeripheral?){
        self.status = .disconnect
        guard let p = peripheral else{ return }
        if self.central.state == .poweredOn{
            self.central.cancelPeripheralConnection(p)
            if self.connectedDevices.contains(p) && self.status != .DFU{
                self.connectedDevices.removeAll{ $0 == p}
            }
        }
    }
    
    /// 添加到已搜索到的设备数组里
    func addSearchDevice(_ peripheral:CBPeripheral?){
        for device in self.searchedDevices{
            if device.identifier.uuidString == peripheral?.identifier.uuidString{
                return
            }
        }
        self.searchedDevices.append(peripheral!)
    }
    
    /// 获取系统底层连接的外围设备，系统底层连接时候，有可能app搜索不到该设备
    func systemConnected()->[CBPeripheral]{
        return self.central.retrieveConnectedPeripherals(withServices:self.services)
    }
}

//MARK: - CBCentralManagerDelegate
extension CCBleManager : CBCentralManagerDelegate
{
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        DispatchQueue.main.async {
            self.status = self.status == .DFU ? self.status : .normal
            self.searchedDevices.removeAll()
            switch central.state {
            case .unknown:
                break
            case .resetting:
                break
            case .unsupported:
                break
            case .unauthorized:
                break
            case .poweredOn:
                self.startScan()
                self.localConnect()
            case .poweredOff:
                self.connectedDevices.removeAll()
                self.status = .connectedFail
            default:
                break
            }
        }
    }
    ///搜索到了设备
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        DispatchQueue.main.async {
            
            ///1.获取外围设备的mac
            let data = advertisementData["kCBAdvDataManufacturerData"] as? NSData ?? NSData()
            peripheral.advertData = data
            peripheral.mac = self.tranToMac(data)
            ///2.过滤/筛选设备
            if self.delegate?.filter?(central, peripheral, advertisementData, RSSI) ?? false{
                self.addSearchDevice(peripheral)
            }
            
            ///3. 其他需要自定义的
            self.delegate?.centralManager?(central, didDiscover: peripheral, advertisementData: advertisementData, rssi: RSSI)
            
            ///实时扫描结果block
            self.scanBlock?()
        }
    }
    
    ///连接失败
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        DispatchQueue.main.async {
            MyPrint("-------------连接失败")
            self.disConnect(peripheral)
            self.status = .connectedFail
            /// 1.断开连接，做数据本地化更新
            self.delegate?.disConnected?(central, didFailToConnect: peripheral, error: error)
            ///2. 其他需要自定义的
            self.delegate?.centralManager?(central, didFailToConnect: peripheral, error: error)
        }
    }
    /// 断开连接
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        DispatchQueue.main.async {
            MyPrint("-------------断开链接")
            if self.status == .connectedSucces{
                self.connectedDevices.removeAll{ $0 == peripheral}
                self.connect(peripheral)
            }
            
            //            self.disConnect(peripheral)
            self.status = .disconnect
            //            /// 1.断开连接，做数据本地化更新
            self.delegate?.disConnected?(central, didFailToConnect: peripheral, error: error)
            ///2. 其他需要自定义的
            self.delegate?.centralManager?(central, didDisconnectPeripheral: peripheral, error: error)
        }
    }
    /// 连接成功
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        DispatchQueue.main.async {
            MyPrint("-------------连接成功 \(peripheral)  mac = \(peripheral.mac)")
            central.stopScan()
            peripheral.delegate = self
            peripheral.discoverServices(nil)///查找所有服务
           let mtu = peripheral.maximumWriteValueLength(for: .withResponse)
           let s = peripheral.maximumWriteValueLength(for:.withoutResponse)
            print("============ mtu = \(mtu)  .. \(s)")
            
            if self.connectedDevices.contains(peripheral) == false && self.status != .DFU{
                self.connectedDevices.append(peripheral)
            }
            
            ///1.连接成功，需要做的本地化数据处理
            self.delegate?.conncted?(central, didConnect: peripheral)
            
            ///2.其他需要自定义的
            self.delegate?.centralManager?(central, didConnect: peripheral)
        }
    }
}

//MARK: - CBPeripheralDelegate
extension CCBleManager:CBPeripheralDelegate
{
    ///搜索到服务
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        self.dataSource?.peripheral?(peripheral, didDiscoverServices: error)
    }
    
    ///搜索到特征值
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        self.dataSource?.peripheral?(peripheral, didDiscoverCharacteristicsFor:service, error: error)
        self.status = self.status == .DFU ? self.status : .connectedSucces
    }
    
    /// 设置通知的回调
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        self.dataSource?.peripheral?(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
    }
    
    ///写数据回调
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        self.dataSource?.peripheral?(peripheral, didWriteValueFor: characteristic, error: error)
    }
    ///数据更新
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        self.dataSource?.peripheral?(peripheral, didUpdateValueFor: characteristic, error: error)
    }
}


extension CCBleManager{
    func tranToMac(_ data:NSData)->String{
        let bytes = [UInt8](data) //byte数组
        var str = ""
        for i in 0..<bytes.count {
            str += String(format:"%02lx:", bytes[i])
        }
        if str.count < 22 {return ""}
        let mac = str[6...22]
        return mac
    }
}

